Rzutowanie typów (casty)

Do czego w zasadzie są te casty

Castowanie typów w C++ służy nam do bezpiecznego zmieniania typu. Kiedyś to robiliśmy poprzez nawias:

#include <iostream>

using namespace std;

int main()
{

    float x = 2.4f;

    int y = (int)x;

    cout << y << endl; // 2
}

Tak się robiło w C. W c++ mamy różna casty

Ważne: Żeby użyć któregokolwiek z poniższych castów, musimy zaincludować biblioteke cstdlib

Do czego jest static_cast?

static_cast to podstawowy cast, który powinien być pierwszym, którego powinniśmy próbować użyć. Tak realnie, robi wszystko to, co cast w stylu C (powyżej), a do tego jest sprawdzany przez kompiler, przez co jest bezpieczniejszy.

static_cast użyjemy wtedy, kiedy jesteśmy pewni, że wiemy co robimy.

Przykłady użycia static_cast:

#include <iostream>
#include <cstdlib>


using namespace std;

int main(){
    double x = 3.445;

    int y = static_cast<int>(x);

    cout << "x: " << x << endl;
    cout << "y: " << y << endl;
}

Do czego jest dynamic_cast?

dynamic_cast używamy głównie przy polimorfizmie i wyłącznie przy wskaźnikach i referencjach obiektów. Najczęściej castujemy typ klasy pochodnej na klasę bazową. Jest to zazwyczaj w pełni bezpieczne, gdyż castujemy typ bardziej specyficzny na typ bardziej generalny.

Przykłady użycia dynamic_cast:

#include <iostream>
#include <cstdlib>

using namespace std;

class A
{
public:
    int a, b;
    A(int _a, int _b) : a(_b), b(_b) {}
    virtual void writeInfo()
    {
        cout << "To jest funkcja z klasy bazowej" << endl;
    }
};

class B : public A
{
public:
    int c;
    B(int _a, int _b, int _c) : A(_a, _b), c(_c) {}
    void writeInfo() override
    {
        cout << "To jest funkcja z klasy pochodnej" << endl;
    }
    void wypiszTylkoWPochodnej()
    {
        cout << "Ta funkcja jest tylko w klasie pochodnej" << endl;
    }
};

int main()
{
    B *obj1 = new B(1, 2, 3);
    A *obj2 = dynamic_cast<A *>(obj1);

    obj1->writeInfo();
    obj1->wypiszTylkoWPochodnej();

    obj2->writeInfo();

    // obj2->wypiszTylkoWPochodnej();
}

Tutaj w powyższym przykładzie mamy dwie klasy:

W funkcji main(), mamy zastosowany dynamic_cast, który przerzuca nam wskaźnik typu B na wskaźnik typu A. I teraz obiekt obj2 zgubił wszystkie rzeczy, których nie ma w klasie A (np. metodę wypiszTylkoWPochodnej())

Do czego jest const_cast?

const_cast jest rzadko używany i jest jedynym castem, który usunie const z typu.

Przykład użycia:

Przykład użycia const_casta:

#include <iostream>

using namespace std;

void wypiszZeSpacjami(string &str)
{
    for (char ch : str)
    {
        cout << ch << " ";
    }
    cout << endl;
}

int main()
{

    const string tekst = "Jakistekstbezspacji";
    cout << tekst << endl;

    // wypiszZeSpacjami(tekst) - BŁĄD
    wypiszZeSpacjami(const_cast<string &>(tekst));
    return 0;
}

Do czego jest reinterpret_cast?

reinterpret_cast jest najrzadziej używanym z tych castów i najczęściej nie powinniśmy go stosować. Używa się go, kiedy dokładnie wiemy co robimy i nie chcemy, żeby kompiler nam cokolwiek sprawdzał.

Najczęściej jest on używany przy nisko poziomowej pracy, kiedy operujemy bezpośrednio na pamięci, a także, kiedy rzutujemy wskaźniki między niepowiązanymi typami.

#include <iostream>

using namespace std;

int main()
{
    int x = 50;

    char *znak = reinterpret_cast<char *>(&x);

    cout << *znak << endl;
}